<?php
/**
 * Created by PhpStorm.
 * User: Mysic
 * Date: 2020/12/28
 * Time: 9:16
 */

namespace app\api\controller;


use app\common\library\MysicLog;
use app\common\mysic\profitShare\Logics;
use app\common\mysic\profitShare\procedures\ConditionJudgment;
use app\common\mysic\profitShare\procedures\DbData;
use app\common\mysic\profitShare\procedures\ProfitOrder;
use app\common\mysic\profitShare\procedures\ProfitShare;
use app\common\mysic\profitShare\procedures\Receives;
use app\common\mysic\profitShare\procedures\RuntimeVariables;
use app\factory\MqFactory;
use app\factory\RedisFactory;

use DateTime;
use org\Redis;
use think\Request;
use think\Controller;
use Throwable;

class ProfitSharing extends Controller
{
    protected $log;
    protected $rmq;
    protected $logics;
    protected $exchangeName = 'new-share';
    protected $refundRoutingKey = 'refund';
    protected $refundQueueName = 'refund';
    protected $shareProfitRoutingKey = 'profitsharing';
    protected $shareProfitQueueName = 'profitsharing';

    public function __construct(Request $request = null)
    {
        parent::__construct($request);
        $this->rmq    = MqFactory::instance();
        $this->log    = MysicLog::instance();
        $this->logics = new Logics(
            new DbData(),
            new ConditionJudgment(),
            new ProfitShare(),
            new Receives(),
            new ProfitOrder()
        );
    }

    public function refund()
    {
        $this->log->setFilename('new-share/refund');
        $processMessage = function ($envelope) {
            try {
                $json = $envelope->getBody();
                $msg  = json_decode($json);
                $this->log->info('===========================================================================================================================');
                $this->log->info($json, '分红开始 - 队列消息内容');
                // 初始化
                $this->logics->dbData->initUserOrderEntity($msg->orderNo);
                $userOrderEntity = $this->logics->dbData->getUserOrderEntity();
                $this->logics->dbData->initWxSettingEntity($userOrderEntity->wxapp_id);
                $wxSettingEntity = $this->logics->dbData->getWxSettingEntity();
                $this->logics->dbData->initReceivesEntitiesByWxAppIdPeriodsNum($userOrderEntity->wxapp_id, $wxSettingEntity->periods_num);
                RuntimeVariables::setReceivesAmount(count($this->logics->dbData->getReceivesEntities()));
                $this->logics->conditionJudgment->init($userOrderEntity, $wxSettingEntity, $this->logics->profitShare);
                if($this->logics->conditionJudgment->isVipCardOrder()) {
                    $this->logics->dbData->initVipCardOrder($userOrderEntity->order_id);
                    //储值卡订单拨比替换分红设置拨比
                    $vipCardOrderEntity = $this->logics->dbData->getVipCardOrderEntity();
                    $this->logics->profitShare->convertVipBonusPercent($userOrderEntity, $wxSettingEntity,
                        $vipCardOrderEntity);
                }
                $this->logics->dbData->initUserEntity($userOrderEntity->user_id);
                $this->logics->dbData->initWxAppEntity($userOrderEntity->wxapp_id);
                $this->logics->dbData->initSystemSetting($userOrderEntity->wxapp_id);
                $wxAppEntity = $this->logics->dbData->getWxAppEntity();
                $this->logics->dbData->initAgentEntity($wxAppEntity->agent_id);
                $this->logics->dbData->initAgentsArray($wxAppEntity->agent_id);
                $this->logics->dbData->initDealerEntity($userOrderEntity->user_id);
                $this->logics->dbData->initWxApiConfigEntity($userOrderEntity->wxapp_id);

                // 完结分账
                if ($this->logics->conditionJudgment->whetherFinishOrderProfitSharing()) {
                    $this->log->info('完结分账');
                    $finishOrderProfitSharingResult = $this->logics->finishOrderProfitSharing();
                    $this->log->info($finishOrderProfitSharingResult, '完结订单分账result');
                    $this->log->info('业务结束');
                    return true;
                }
                // 用户升级为微股东
                if ($this->logics->conditionJudgment->userUpgradable()) {
                    $this->log->info('用户升级为微股东');
                    $userUpgradeResult = $this->logics->userUpgrade();
                    $this->log->info(bool2str($userUpgradeResult), '用户升级微股东result');
                    if (!$userUpgradeResult) {
                        $this->log->error('用户升级微股东失败');
                        $this->log->info('业务结束');
                        return true;
                    }
                    // 将用户支付的订单插入到订单分红列表中
                    $this->log->info('创建订单分红信息');
                    $createReceiveResult = $this->logics->createReceive();
                    $this->log->info(bool2str($createReceiveResult),'创建订单分红信息result');
                    if (!$createReceiveResult) {
                        $this->log->error('本次订单加入分红队列失败');
                        $this->log->info('业务结束');
                        return true;
                    }
                }
                // 创建分红明细
                if ($this->logics->conditionJudgment->selfBonusAble()) {
                    $this->log->info('本次订单分红创建分红流水明细');
                    $receivesUserAppendResult = $this->logics->createReceivesLog(ProfitShare::API_TYPE_PROFIT_SHARING);
                    $this->log->info(bool2str($receivesUserAppendResult), '本次订单分红创建分红流水明细result');
                    if (!$receivesUserAppendResult) {
                        $this->log->error('添加订单分账流水失败');
                        $this->log->info('业务结束');
                        return true;
                    }
                }
                //创建 平台 & 服务商 分账流水单
                $this->log->info('创建平台 & 服务商分账流水单');
                $result = $this->logics->createSystemServiceProfitOrders();
                $this->log->info(bool2str($result), '创建平台 & 服务商分账流水单result');
                if (!$result) {
                    $this->log->error('创建平台与服务商分账流水单失败');
                    $this->log->info('业务结束');
                    return true;
                }

                //创建商户分账流水单
                $this->log->info('创建商户分账流水单');
                $result = $this->logics->createStoreProfitOrder();
                $this->log->info(bool2str($result), '创建商户分账流水单result');
                if (!$result) {
                    $this->log->error('创建商户分账流水单失败');
                    $this->log->info('业务结束');
                    return true;
                }
                //给其他未出局的微股东创建分红流水数据
                if ($this->logics->conditionJudgment->otherBonusAble()) {
                    $this->log->info('给其他未出局的微股东进行分红');
                    $currentReceivesEntities = RuntimeVariables::getReceivesEntities();
                    $otherReceivesEntities = $this->logics->dbData->getReceivesEntities();
                    // 将其他未出局微股东的分红主体信息设置到全局变量中
                    RuntimeVariables::setReceivesEntities($otherReceivesEntities);
                    $this->log->info('给其他未出局的分红创建分红流水明细');
                    $createReceivesLogResult = $this->logics->createReceivesLog();
                    //将本次支付订单的分红主体信息与其他未出局微股东的分红主体信息合并
                    $receivesEntities = array_merge($currentReceivesEntities, $otherReceivesEntities);
                    RuntimeVariables::setReceivesEntities($receivesEntities);
                    $logArr = [];
                    foreach($receivesEntities as $entity) {
                        $logArr[] = $entity->toArray();
                    }
                    $this->log->info($logArr, '全部的分红主体集合');
                    $this->log->info(bool2str($createReceivesLogResult), '给其他未出局的分红创建分红流水明细result');
                    $msg = $this->logics->makeProfitSharingMessages(RuntimeVariables::getReceivesEntities(), $userOrderEntity);
                    $this->log->info(json_encode($msg), '入分账队列数据');
                    $this->rmq->send($msg, $this->exchangeName, $this->shareProfitRoutingKey, $this->shareProfitQueueName);
                    $this->log->info('业务结束');
                    return true;
                }
                // 只给服务商分账
                $msg = [
                    'order_no'          => $userOrderEntity->order_no,
                    'receives_order_no' => [],
                ];
                // 给自己分账
                if($this->logics->conditionJudgment->selfBonusAble()) {
                    $msg['receives_order_no'][] = $userOrderEntity->order_no;
                }
                $this->log->info($msg, '入分账队列数据');
                $this->rmq->send($msg, $this->exchangeName, $this->shareProfitRoutingKey, $this->shareProfitQueueName);
                $this->log->info('业务结束');
                return true;
            } catch (Throwable $e) {
                $this->log->error(exceptionLogFormat($e), '消息队列 - 消费抛出异常');
                $this->log->info('业务结束');
                return true;
            }
        };
        $this->rmq->run($processMessage, $this->exchangeName, $this->refundRoutingKey, $this->refundQueueName);
    }

    public function sharing()
    {
        $this->log->setFilename('new-share/profitsharing');
        $processMessage = function ($envelope) {
            try {
                $json = $envelope->getBody();
                $msg  = json_decode($json);
                $this->log->info('===========================================================================================================================');
                $this->log->info($json, '分红分账开始 - 队列消息内容');
                //初始化当前支付订单实体
                $this->logics->dbData->initUserOrderEntity($msg->order_no);
                //初始化商户分红设置实体
                $paidUserOrderEntity = $this->logics->dbData->getUserOrderEntity();
                $this->logics->dbData->initWxSettingEntity($paidUserOrderEntity->wxapp_id);
                $this->logics->dbData->initSystemSetting($paidUserOrderEntity->wxapp_id);
                //初始化微信分账接口参数
                $this->logics->profitShare->abonusApi->wxConfig($paidUserOrderEntity['wxapp_id']);
                //初始化平台分账订单实体
                $this->logics->dbData->initSystemProfitOrderEntity($msg->order_no);
                RuntimeVariables::setSystemProfitOrderEntity($this->logics->dbData->getSystemProfitOrderEntity());
                //初始化服务商分账订单实体列表
                $this->logics->dbData->initServiceProfitOrderEntities($msg->order_no);
                RuntimeVariables::setServiceProfitOrderEntities($this->logics->dbData->getServiceProfitOrderEntities());
                //初始化商户分账订单实体
                $this->logics->dbData->initStoreProfitOrderEntity($msg->order_no);
                RuntimeVariables::setStoreProfitOrderEntity($this->logics->dbData->getStoreProfitOrderEntity());

                $receivers = [];
                $result = $this->logics->profitShare->addSystemProfitSharingReceiver('1605578391', '西安微股客科技发展有限公司');
                if(!$result) {
                    $this->log->error('添加系统分账失败');
                    $this->log->info('业务结束');
                    return true;
                }
                $profit = RuntimeVariables::getSystemProfitOrderEntity()->abonus;
                $systemSetting = $this->logics->dbData->getSystemSetting();
                $systemProfitSharingReceivers = [
                    'type' => 'MERCHANT_ID',
                    'account' => $systemSetting['mch_id'],
                    'amount' => intval(bcmul(strval($profit), '100')),
                    'description' => '分账给系统服务商'
                ];
                array_push($receivers, $systemProfitSharingReceivers);

                // 初始化申请分账所需的数据
                if($msg->receives_order_no) {
                    //初始化订单分红实体列表
                    $this->logics->dbData->initReceivesEntitiesByReceivesOrderNos($msg->receives_order_no);
                    RuntimeVariables::setReceivesEntities($this->logics->dbData->getReceivesEntities());
                    //初始化分红流水实体列表
                    $this->logics->dbData->initReceivesLogEntitiesByPaidOrderNo($msg->order_no);
                    RuntimeVariables::setReceivesLogEntities($this->logics->dbData->getReceivesLogEntities());
                    //初始化分红订单的用户实体
                    $this->logics->dbData->initUserEntitiesByReceivesEntities(RuntimeVariables::getReceivesEntities());
                    RuntimeVariables::setUserEntities($this->logics->dbData->getUserEntities());
                    //初始化分红用户的分红订单实体列表
                    $this->logics->dbData->initReceiveOrderEntities($msg->receives_order_no);
                    RuntimeVariables::setReceiveOrderEntities($this->logics->dbData->getReceiveOrderEntities());
                    //用户分红订单已到期，分红余额转零钱  bonus_type = BONUS_BALANCE  更新为分红信息为已过期
                    $this->logics->profitShareBalanceToWallet(
                        RuntimeVariables::getReceivesEntities(),
                        RuntimeVariables::getReceivesLogEntities(),
                        RuntimeVariables::getUserEntities(),
                        $this->logics->dbData->getWxSettingEntity()
                    );
                    //初始化请求分账接口的接收人参数
                    $profitSharingReceivers = $this->logics->profitShare->makeProfitSharingApplyReceiversByUser(
                        RuntimeVariables::getReceivesEntities(),
                        RuntimeVariables::getUserEntities(),
                        RuntimeVariables::getReceivesLogEntities()
                    );
                    $this->log->info($profitSharingReceivers, '请求分账接口分账接收人列表（不含系统分红）');
                    $receivers = array_merge($receivers, $profitSharingReceivers);
                    // 添加分账接收人
                    $this->logics->profitShare->addProfitSharingReceivers($profitSharingReceivers);
                }
                //分账分页
                $paginatedReceivers = $this->logics->paginateProfitSharingReceivers($receivers);
                foreach($paginatedReceivers as $receivers) {
                    // 分账
                    $this->logics->profitSharing($receivers);
                }
                if($msg->receives_order_no) {
                    //更新订单分红信息
                    $this->log->info('开始', '更新订单分红信息');
                    $this->logics->updateReceives();
                    //更新分红流水状态
                    $this->log->info('开始', '更新分红流水状态');
                    $this->logics->updateReceivesLog();
                }

                //更新平台分账流水状态
                $this->log->info('开始', '更新平台分账流水状态');
                $this->logics->updateSystemProfitOrder();
                //更新服务商分账流水状态
                $this->log->info('开始', '更新服务商分账流水状态');
                $this->logics->updateServiceProfitOrder();
                //更新商户分账流水状态
                $this->log->info('开始', '更新商户分账流水状态');
                $this->logics->updateStoreProfitOrder();
                //完结分账
                $this->log->info('开始','完结订单分账');
                $this->logics->finishOrderProfitSharing();
                $this->log->info('业务结束');
                return true;
            } catch (Throwable $e) {
                $this->log->error(exceptionLogFormat($e), '消息队列 - 消费抛出异常');
                $this->log->info('业务结束');
                return true;
            }
        };
        $this->rmq->run($processMessage, $this->exchangeName, $this->shareProfitRoutingKey,
            $this->shareProfitQueueName);
    }
    // 每日凌晨0时1分执行
    public function cronPushExpireToRedis()
    {
        $expireStatusKey = 'order_expire_crontab_run_status';
        $expireOrderKey = 'expire_order';
        $redis = new Redis();
        $dateTime = new DateTime();
        $dateTime->setTime(23,59,59);
        $todayDate = date('Ymd');
        $todayEndTimestamp = $dateTime->getTimestamp();
        $expireIsExist = $redis->exists($expireStatusKey, 'hash', $todayDate);
        if( ! $expireIsExist) {
            $redis->hset($expireStatusKey, $todayDate, 'ready');
        }

        if($redis->hget($expireStatusKey, [$todayDate])[$todayDate] == 'finish') {
            return true;
        }

        $expireReceives = db('divide_receives')->where('type', 0)->whereTime('finish_time', '<=', $todayEndTimestamp)
            ->whereTime('finish_time', '>', 0)->order('finish_time', 'asc')
            ->field('order_no,wxapp_id,finish_time')->select()->toArray();

        if($expireReceives) {
            $wxAppIds = array_column($expireReceives, 'wxapp_id');
            $wxAppIds = array_unique($wxAppIds);
            $wxApps = db('wxapp')->whereIn('wxapp_id', $wxAppIds)->select()->toArray();
            $weigukeReceives = [];
            foreach($wxApps as $wxApp) {
                if($wxApp['service_type'] == 2) {
                    $weigukeReceives[] = $wxApp['wxapp_id'];
                }
            }
            foreach($expireReceives as $receive) {
                if( ! in_array($receive['wxapp_id'], $weigukeReceives)) {
                    continue;
                }
                $redis->rpush($expireOrderKey, $receive['finish_time'] . '-' .  $receive['order_no']);
            }
        }
        $redis->hset($expireStatusKey, $todayDate, 'finish');
        return true;
    }

    public function expire()
    {
        $this->log->setFilename('order_expire');
        $this->log->info('===========================================================================================================================');
        $expireOrderKey = 'expire_order';
        $sleepSecond = 1;
        $redis = new Redis();
        try {
            while(true){
                $expireOrderCount = $redis->llen($expireOrderKey);
                if( ! $expireOrderCount) {
                    sleep($sleepSecond);
                    continue;
                }
                $string = $redis->lindex($expireOrderKey, 0);
                $data = explode('-', $string);

                $finishTimestamp = $data[0];
                if($finishTimestamp > time()) {
                    sleep($sleepSecond);
                    continue;
                }
                $orderNo = $data[1];
                $this->logics->dbData->initReceivesEntitiesByReceivesOrderNos([$orderNo]);
                $receivesEntities = $this->logics->dbData->getReceivesEntities();
                RuntimeVariables::setReceivesEntities($receivesEntities);
                $receiveEntity = current($receivesEntities);
                if($receiveEntity->finish_time > time()) {
                    sleep($sleepSecond);
                    continue;
                }
                RuntimeVariables::setProfit($receiveEntity->balance);
                $this->logics->dbData->initWxSettingEntity($receiveEntity->wxapp_id);
                $this->logics->dbData->initUserEntitiesByReceivesEntities($receivesEntities);
                RuntimeVariables::setUserEntities($this->logics->dbData->getUserEntities());
                $this->logics->createReceivesLog();
                $this->logics->dbData->initReceivesLogEntitiesByPaidOrderNo('SYSTEM');
                RuntimeVariables::setReceivesLogEntities($this->logics->dbData->getReceivesLogEntities());
                $this->logics->profitShareBalanceToWallet(
                    RuntimeVariables::getReceivesEntities(),
                    RuntimeVariables::getReceivesLogEntities(),
                    RuntimeVariables::getUserEntities(),
                    $this->logics->dbData->getWxSettingEntity()
                );
                $ret = $redis->lpop($expireOrderKey);
                if('nil' == $ret) {
                    sleep($sleepSecond);
                    continue;
                }
            }
            //结束
        } catch(Throwable $t){
            $this->log->info(exceptionLogFormat($t), '运行抛出了异常');
            sleep($sleepSecond);
        }
    }
}